import { z } from "zod"; import { withUser, parseBody, json, ApiError } from "@/lib/api"; import { duplicateConversation, exportConversation, shareConversation, revokeShare, revokeShareById, getShare, listShares, deleteMessage, EXPORT_FORMATS } from "@/lib/conversations/service"; import { LIMITS } from "@/lib/rate-limit"; import { APP_URL } from "@/lib/env"; export const dynamic = "force-dynamic"; type P = { id: string }; const schema = z.discriminatedUnion("action", [ z.object({ action: z.literal("duplicate"), title: z.string().max(200).optional() }), z.object({ action: z.literal("branch"), messageId: z.string().max(64), title: z.string().max(200).optional() }), /** Prefer `GET /api/conversations/[id]/export?format=` for downloads; kept for existing callers. */ z.object({ action: z.literal("export"), format: z.enum(EXPORT_FORMATS) }), /** `messageIds` → share only those messages (new link). Without it → the conversation's single full link (upsert). */ z.object({ action: z.literal("share"), messageIds: z.array(z.string().max(64)).max(500).optional() }), /** Revoke every active link of the conversation, or one link when `shareId` is given. */ z.object({ action: z.literal("unshare"), shareId: z.string().max(64).optional() }), z.object({ action: z.literal("share-status") }), z.object({ action: z.literal("list-shares") }), z.object({ action: z.literal("delete-message"), messageId: z.string().max(64) }), ]); export const POST = withUser

( async ({ req, user }, { id }) => { const body = await parseBody(req, schema); switch (body.action) { case "duplicate": return json({ conversation: await duplicateConversation(user.id, id, { title: body.title }) }, { status: 201 }); case "branch": return json({ conversation: await duplicateConversation(user.id, id, { uptoMessageId: body.messageId, title: body.title }) }, { status: 201 }); case "export": { const out = await exportConversation(user.id, id, body.format, { appUrl: APP_URL }); return new Response(out.body, { headers: { "Content-Type": out.contentType, "Content-Disposition": `attachment; filename="${out.filename}"` } }); } case "share": { const res = await shareConversation(user.id, id, { messageIds: body.messageIds }); return json({ ...res, path: `/share/${res.id}` }, { status: res.created ? 201 : 200 }); } case "unshare": if (body.shareId) await revokeShareById(user.id, body.shareId); else await revokeShare(user.id, id); return json({ ok: true }); case "share-status": return json({ share: await getShare(user.id, id), shares: await listShares(user.id, id) }); case "list-shares": return json({ shares: await listShares(user.id, id) }); case "delete-message": await deleteMessage(user.id, id, body.messageId); return json({ ok: true }); default: throw new ApiError(400, "Unknown action"); } }, { limit: { ...LIMITS.share, key: "conv-actions" } }, );